package com.jzhao.gper.spring.v1;

import com.jzhao.gper.spring.v1.annotation.JZAutowired;
import com.jzhao.gper.spring.v1.annotation.JZController;
import com.jzhao.gper.spring.v1.annotation.JZRequestMapping;
import com.jzhao.gper.spring.v1.annotation.JZService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class JZDispatcherServlet extends HttpServlet {

    //存放包扫描路径下的所有class
    private List<String> classNames = new ArrayList<String>();
    //IOC容器
    private Map<String,Object> ioc = new HashMap<String,Object>();
    //handlerMapping
    private Map<String,Object> handlerMapping = new HashMap<String,Object>();

    //配置文件对象
    Properties properties =null;

    public JZDispatcherServlet() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            resp.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatch(HttpServletRequest req,HttpServletResponse res) throws Exception{
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        if(!this.handlerMapping.containsKey(url)){res.getWriter().write("404 Not Found!!");return;}
        Method method = (Method) this.handlerMapping.get(url);
        Map<String,String[]> params = req.getParameterMap();
        Object instance = this.ioc.get(toLowerFirst(method.getDeclaringClass().getSimpleName()));
        method.invoke(instance,new Object[]{req,res});

    }

    @Override
    public void init(ServletConfig config) throws ServletException {

        try{
            //1、加载配置
            loadConfig(config);

            //2、扫描包下所有class
            doScanner(properties.getProperty("scanpackage"));

            //3、将引用注解的class，实例化到IOC容器
            doInstances();

            //4、实现依懒注入
            doAutowired();

            //5、handlerMapping
            doHandlerMapping();

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void doHandlerMapping() {
        if(ioc.isEmpty()){return;}

        for (Map.Entry<String, Object> entry:ioc.entrySet()) {
            Class<?>  beanClass = entry.getValue().getClass();
            if(!beanClass.isAnnotationPresent(JZController.class)){continue;}

            String baseUrl = beanClass.getAnnotation(JZRequestMapping.class).value();

            Method[] methods = beanClass.getMethods();
            for (Method method : methods) {
                if(!method.isAnnotationPresent(JZRequestMapping.class)){continue;}
                JZRequestMapping  requestMapping= method.getAnnotation(JZRequestMapping.class);
                baseUrl = (baseUrl +"/" +requestMapping.value()).replaceAll("/+","/");
                handlerMapping.put(baseUrl,method);
            }
        }
    }

    /**
     * 自动注入
     */
    private void doAutowired() throws IllegalAccessException {
        if(ioc.isEmpty()){return;}

        for (Map.Entry<String, Object> entry:ioc.entrySet()) {
            Class beanClass = entry.getValue().getClass();

            Field[] fields = beanClass.getDeclaredFields();

            for (Field field: fields) {
                //如果没有自动注入继续
                if(!field.isAnnotationPresent(JZAutowired.class)){continue;}

                JZAutowired jzAutowired = field.getAnnotation(JZAutowired.class);
                String diName = jzAutowired.value();
                if(null==diName || diName.trim().isEmpty()){
                    diName = field.getType().getName();
                }

                field.setAccessible(true);
                field.set(entry.getValue(),ioc.get(diName));
            }
        }
    }

    /**
     * 实例化对象
     */
    private void doInstances() throws Exception {
        if(classNames.isEmpty()){return;}

        for (String className: classNames) {
            Class<?> clazz = Class.forName(className);
            //判断是否controller注解
            if(clazz.isAnnotationPresent(JZController.class)){
                //controller定义了name
                JZController jzController = clazz.getAnnotation(JZController.class);
                String beanName = jzController.value();
                //如果没有定义,则类名首字母小写
                if(null==beanName || beanName.trim().equals("") ){
                    beanName = toLowerFirst(clazz.getSimpleName());
                }
                ioc.put(beanName,clazz.newInstance());
            }
            //判断是否是Service注解
            else  if(clazz.isAnnotationPresent(JZService.class)){
                //Service定义了name
                JZService jZService = clazz.getAnnotation(JZService.class);
                String beanName = jZService.value();
                //如果没有定义,则类名首字母小写
                if(null==beanName || beanName.trim().equals("")){
                    beanName = toLowerFirst(clazz.getSimpleName());
                }
                ioc.put(beanName,clazz.newInstance());

                //将其实现的接口，也实使化，以接口全类名做为beanName
                for (Class inf:clazz.getInterfaces()) {
                    String infBeanName = inf.getName();
                    //一个接口，可能有多个实现,如
                    if(ioc.containsKey(infBeanName)){
                        System.out.println(infBeanName+ " 已经存在！");
                        continue;
                    }
                    ioc.put(infBeanName,clazz.newInstance());
                }
            }
        }
    }

    private String toLowerFirst(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }

    /**
     * 加载配置文件
     * @param config
     */
    private void loadConfig(ServletConfig config) {
        InputStream in = null;
        try{
            //1、加载配置
            properties = new Properties();
            String applicationName =  config.getInitParameter("contextConfigLocation");
            in = this.getClass().getClassLoader().getResourceAsStream(applicationName);
            properties.load(in);
        } catch (Exception e){
            e.printStackTrace();
        }
        finally {
            if(in !=null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 扫描包下，所有class文件
     * @param scanpackage
     */
    private void doScanner(String scanpackage) {
        //获取绝对路径
        URL url = this.getClass().getClassLoader().getResource("/"+scanpackage.replaceAll("\\.","/"));
        File classDir = new File(url.getFile());
        for (File file:classDir.listFiles()) {
            if(file.isDirectory()){
                doScanner(scanpackage+"."+file.getName());
            } else {
                //不是class文件，跳过，继续
                if(!file.getName().endsWith(".class")){continue;}
                String className = scanpackage+"."+file.getName().replace(".class","");
                classNames.add(className);
            }
        }
    }
}
